package org.dromara.system.kernel;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.nacos.shaded.com.google.common.collect.Lists;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.system.domain.*;
import org.dromara.system.mapper.*;
import org.dromara.system.mq.EmailSender;
import org.dromara.system.service.*;
import org.dromara.system.utils.SendEmailUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.mail.*;
import javax.mail.internet.MimeMessage;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 发送邮件核心处理类
 */
@Slf4j
@Service
@RefreshScope
@RequiredArgsConstructor
public class SendEmailKernel {

    private final IEmailAccountService accountService;
    private final IEmailListService emailListService;
    private final IPayOrderService payOrderService;
    private final EmailQueueMapper emailQueueMapper;
    private final EmailAccountMapper emailAccountMapper;
    private final EmailListMapper emailListMapper;
    private final EmailTemplateMapper emailTemplateMapper;
    private final SendEmail2Kernel sendEmail2Kernel;
    @Value("${useNewOrderEmail}")
    public Boolean useNewOrderEmail; //是否启用新的订单邮件逻辑

    // 创建一个固定大小的线程池
    private static final ExecutorService executor = Executors.newFixedThreadPool(10);

    /**
     * 异步发送邮件
     *
     * @param queue  邮件队列
     * @param emails 邮件
     */
    @Async
    public void asyncSendEmail(EmailQueue queue, List<EmailList> emails) {
        log.info("队列：" + queue.getTitle() + "  开始异步处理！");

        if (emails == null || emails.isEmpty()) {
            EmailQueue upObj = new EmailQueue();
            upObj.setQueueId(queue.getQueueId());
            upObj.setStatus(3);
            upObj.setErrTime(null);
            upObj.setErrContent("此队列没有需要发送的邮件");
            upObj.setCompleteTime(DateUtils.getNowDate());
            emailQueueMapper.updateById(upObj);
            log.info("此队列没有需要发送的邮件，直接更新为完成状态！");
            return;
        }

        //获取正常的发件邮箱配置，异常的不会查询出来
        Long agentId = queue.getAgentId() == null ? queue.getCreateId() : queue.getAgentId();
        List<EmailAccount> accountList = accountService.selectAccountList(Arrays.stream(queue.getSendEmailId().split(",")).map(Long::valueOf).toList(), agentId, null);

        if (accountList.isEmpty()) {
            //更新队列状态
            EmailQueue upObj = new EmailQueue();
            upObj.setQueueId(queue.getQueueId());
            upObj.setStatus(4);
            upObj.setErrTime(DateUtils.getNowDate());
            upObj.setErrContent("没有可用的发件邮箱，请检查邮箱账号配置");
            upObj.setUpdateTime(DateUtils.getNowDate());
            emailQueueMapper.updateById(upObj);
            emailListService.updateEmailStatusByQueueId(queue.getQueueId(), 2, 4);
            log.info("没有可用的发件邮箱，请检查邮箱账号配置");
            return;
        }

        //查询物流发件模板
        EmailTemplate template = emailTemplateMapper.selectById(queue.getTemplateId());
        if (template == null) {
            //更新队列状态
            EmailQueue upObj = new EmailQueue();
            upObj.setQueueId(queue.getQueueId());
            upObj.setStatus(4);
            upObj.setErrTime(DateUtils.getNowDate());
            upObj.setErrContent("未查询到物流邮件模板，请核对");
            upObj.setUpdateTime(DateUtils.getNowDate());
            emailQueueMapper.updateById(upObj);
            emailListService.updateEmailStatusByQueueId(queue.getQueueId(), 2, 4);
            log.info("未查询到物流邮件模板，请核对");
            return;
        }

//        if (emailQueue.getDelaySendNum() == null) {
//            emailQueue.setDelaySendNum(100);
//        }
        if (queue.getDelayTime() == null) {
            queue.setDelayTime(5);
        }

        List<Future<Boolean>> futures = new ArrayList<>();

        //分组条数
        int size;

        //是否开启延迟发送
        if(Objects.equals(queue.getIsDelay(), 1)) {
            size = queue.getDelaySendNum();
        } else {
            size = emails.size() / accountList.size() + (emails.size() % accountList.size() > 0 ? 1 : 0);
        }

        //分组邮件
        List<List<EmailList>> groupEmpl = Lists.partition(emails, size);

        //循环账号发送
        int index = 0;
        for (EmailAccount account : accountList) {
            if (index >= groupEmpl.size()) {
                break;
            }
            List<EmailList> emplList = groupEmpl.get(index);
            if (emplList != null && !emplList.isEmpty()) {
                //多线程发送
                futures.add(executor.submit(() -> sendEmail(queue, account, template, emplList)));
            }
            index += 1;
        }

        boolean hasTrue = Boolean.FALSE;
        for (int i = 0; i < futures.size(); i++) {
            Future<Boolean> future = futures.get(i);
            try {
                Boolean result = future.get();
                if (result) {
                    hasTrue = Boolean.TRUE;
                }
                log.info("线程" + i + "：执行完成！" + result);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        emailListService.updateEmailStatusByQueueId(queue.getQueueId(), 2, 4);

        //如果没有一个账号执行成功，说明所有账号都异常了，直接把队列修改成失败
        if (!hasTrue) {
            EmailQueue upObj = new EmailQueue();
            upObj.setQueueId(queue.getQueueId());
            upObj.setStatus(4);
            upObj.setErrTime(DateUtils.getNowDate());
            emailQueueMapper.updateById(upObj);
            return;
        }

//        //查询队列中待发送和发送中的邮件
//        Long sending = emailListMapper.selectCount(new LambdaQueryWrapper<EmailList>().eq(EmailList::getQueueId, queue.getQueueId()).in(EmailList::getStatus, Arrays.asList(1, 2)));
//        //没有待发送和发送中的邮件
//        if(sending == 0) {
//            return;
//        }

        Long fail = emailListMapper.selectCount(new LambdaQueryWrapper<EmailList>().eq(EmailList::getQueueId, queue.getQueueId()).eq(EmailList::getStatus, 4));
        if (fail > 0) {
            // 有失败的，则队列直接失败
            EmailQueue upObj = new EmailQueue();
            upObj.setQueueId(queue.getQueueId());
            upObj.setStatus(4);
            upObj.setErrTime(DateUtils.getNowDate());
            upObj.setErrContent("部分邮件发送失败，详情查看邮件列表");
            emailQueueMapper.updateById(upObj);
        } else {
            // 没有有失败的，则认为发送完成
            LambdaUpdateWrapper<EmailQueue> update = new LambdaUpdateWrapper<EmailQueue>().eq(EmailQueue::getQueueId, queue.getQueueId());
            update.set(EmailQueue::getStatus, 3);
            update.set(EmailQueue::getErrTime, null);
            update.set(EmailQueue::getErrContent, "");
            update.set(EmailQueue::getCompleteTime, DateUtils.getNowDate());
            emailQueueMapper.update(update);
            log.info("队列：" + queue.getQueueId() + "  处理完成！");
        }

//        EmailQueue newObj = emailQueueMapper.selectById(queue.getQueueId());
//        if (newObj.getStatus() == 2) {
//            //进入下一次循环
//            emailSender.sendEmailMessage(queue.getQueueId(), queue.getDelayTime() * 60 * 1000);
//        }
    }

    private Boolean sendEmail(EmailQueue queue, EmailAccount account, EmailTemplate template, List<EmailList> emList) {

        //设置邮件服务器配置
        SendEmailUtils.EmailServerCfg cfg = new SendEmailUtils.EmailServerCfg();
        cfg.setAccount(account.getAccount());
        cfg.setPassword(account.getPassword());
        cfg.setHost(account.getServer());
        cfg.setPort(account.getPort());
        cfg.setProtocol(account.getAgreement());
        cfg.setEnableSsl(account.getEnableSsl());

        //构建邮件服务session
        Session session = SendEmailUtils.getSession(cfg);

        // 4. 根据 Session 获取邮件传输对象
        Transport transport;
        try {
            transport = session.getTransport();
            transport.connect(account.getAccount(), account.getPassword());
        } catch (Exception e) {
            log.error("根据 Session 获取邮件传输对象异常", e);
            //更新队列状态
            EmailQueue upObj = new EmailQueue();
            upObj.setQueueId(queue.getQueueId());

            String msg = "获取邮件传输对象异常：";
            if (e.getMessage().contains("No provider for")) {
                msg = "邮件协议有误：";
            }

            if (e.getMessage().contains("Couldn't connect to host")) {
                msg = "邮件服务器地址有误：";
            }

            upObj.setErrContent(msg + e.getMessage());
            upObj.setErrTime(DateUtils.getNowDate());
            upObj.setUpdateTime(DateUtils.getNowDate());
            emailQueueMapper.updateById(upObj);

            //更正账号状态
            EmailAccount upAc = new EmailAccount();
            upAc.setId(account.getId());
            upAc.setStatus(1);
            emailAccountMapper.updateById(upAc);

            return Boolean.FALSE;
        }

        try {
            for (EmailList email : emList) {
                email.setSendTime(DateUtils.getNowDate());
                email.setSendEmailId(account.getId());
                email.setSendEmailAct(account.getAccount());

                PayOrder order = payOrderService.selectById(email.getOrderId());

                //处理邮件填充内容
                Pair<String, String> pair = sendEmail2Kernel.getEmailContent(email, template, order);
                String content = pair.getRight();

                if (StringUtils.isNotEmpty(content)) {
                    //构建邮件消息对象
                    MimeMessage message = SendEmailUtils.buildMessage(cfg, session, email.getReceiveEmail(), pair.getLeft(), content);

                    //发送邮件
                    if (message != null) {
                        try {
                            // 6. 发送邮件, 发到所有的收件地址, message.getAllRecipients() 获取到的是在创建邮件对象时添加的所有收件人, 抄送人, 密送人
                            transport.sendMessage(message, message.getAllRecipients());
                            email.setStatus(3);

                            //更新订单邮件状态
                            PayOrder obj = new PayOrder();
                            obj.setOrderId(order.getOrderId());
                            if (Objects.equals(email.getType(), 1)) {
                                obj.setPayEmail(1);
                            } else if(Objects.equals(email.getType(), 2)) {
                                obj.setTrackEmail(1);
                            }
                            payOrderService.updatePayOrderAsync(obj);

                        } catch (MessagingException e) {
                            e.printStackTrace();
                            String msg = e.getMessage();
                            if (msg != null && msg.contains("Recipient address rejected: Email Sending Quota Exceeded")) {
                                msg = msg + "【收件人拒绝】";
                            }
                            email.setErrContent(msg);
                            email.setStatus(4);
                            email.setErrTime(DateUtils.getNowDate());
                        }
                    }
                } else {
                    email.setStatus(4);
                    email.setErrTime(DateUtils.getNowDate());
                    email.setErrContent("邮件内容为空，无法发送");
                }

                //更新邮件状态
                email.setUpdateTime(DateUtils.getNowDate());
                emailListMapper.updateById(email);
                if (email.getStatus() == 3) {
                    emailListMapper.update(new LambdaUpdateWrapper<EmailList>().eq(EmailList::getEmailId, email.getEmailId()).set(EmailList::getErrTime, null).set(EmailList::getErrContent, null));
                }
            }

            // 7. 关闭连接
            transport.close();
            return Boolean.TRUE;
        } catch (MessagingException e) {
            e.printStackTrace();
            return Boolean.FALSE;
        }
    }

}
